2026-06-05 19:14:54 +02:00
|
|
|
import 'dart:io';
|
|
|
|
|
|
2026-06-04 08:17:12 +02:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
|
import 'package:ocideck/l10n/app_localizations.dart';
|
|
|
|
|
|
|
|
|
|
void main() {
|
|
|
|
|
tearDown(() => AppLocalizations.setActiveLanguageCode('nl'));
|
|
|
|
|
|
|
|
|
|
test('supports Frisian and Papiamento language choices', () {
|
|
|
|
|
expect(AppLocalizations.languageNames['fy'], 'Frysk');
|
|
|
|
|
expect(AppLocalizations.languageNames['pap'], 'Papiamento');
|
|
|
|
|
expect(AppLocalizations.supportedLocales, contains(const Locale('fy')));
|
|
|
|
|
expect(AppLocalizations.supportedLocales, contains(const Locale('pap')));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('uses app translations while Material falls back safely', () {
|
|
|
|
|
AppLocalizations.setActiveLanguageCode('fy');
|
|
|
|
|
expect(const AppLocalizations(Locale('en')).t('settings'), 'Ynstellingen');
|
|
|
|
|
expect(
|
|
|
|
|
const AppLocalizations(Locale('en')).d('Toetsenlegenda'),
|
|
|
|
|
'Toetsleginda',
|
|
|
|
|
);
|
|
|
|
|
expect(AppLocalizations.materialLocaleFor('fy'), const Locale('en'));
|
|
|
|
|
|
|
|
|
|
AppLocalizations.setActiveLanguageCode('pap');
|
|
|
|
|
expect(
|
|
|
|
|
const AppLocalizations(Locale('en')).t('settings'),
|
|
|
|
|
'Preferensianan',
|
|
|
|
|
);
|
|
|
|
|
expect(
|
|
|
|
|
const AppLocalizations(Locale('en')).d('Toetsenlegenda'),
|
|
|
|
|
'Legenda di tekla',
|
|
|
|
|
);
|
|
|
|
|
expect(AppLocalizations.materialLocaleFor('pap'), const Locale('en'));
|
|
|
|
|
});
|
2026-06-05 19:14:54 +02:00
|
|
|
|
|
|
|
|
test('all literal Dutch source strings have an English fallback', () {
|
|
|
|
|
AppLocalizations.setActiveLanguageCode('en');
|
|
|
|
|
|
|
|
|
|
const unchangedInEnglish = {
|
|
|
|
|
'Accent / bullets',
|
|
|
|
|
'Bullet',
|
|
|
|
|
'Coverflow',
|
Add project docs, EUPL licence, and open-source licence check
Documentation & licensing:
- Add the EUPL-1.2 licence (LICENSE.md) and set the project licence; refresh
the README (name origin wink, updated feature list, documentation index).
- Add CONTRIBUTING, SECURITY, CODE_OF_CONDUCT, CHANGELOG, AUTHORS, and
THIRD_PARTY_NOTICES, plus docs/ (ARCHITECTURE, BUILD, USER_GUIDE, SHORTCUTS,
LICENSE_COMPLIANCE) and .github/ (CI workflow, issue/PR templates).
- Bring docs/FILE_FORMAT.md in line with current behaviour (code & chart
slides, per-slide TLP comment, annotation .ink.json sidecar, chart data/ CSVs).
Open-source compliance:
- Add tool/check_licenses.dart and a `make licenses` target (wired into
check-full and CI) that verifies every resolved dependency uses a recognised
open-source licence. A scan of all 151 packages and bundled assets found only
OSI-approved licences.
Charts (Fase 1.1):
- Replace the chart CSV textarea with an in-app editable data grid (editable
series/labels/values, add/remove row & column, read-only when linked).
- Centralize the linked-CSV directory name (`data/`) in a shared constant.
Also normalize formatting repo-wide with `dart format` and fix one
curly-braces lint, so `make check` and CI are green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 12:19:56 +02:00
|
|
|
'Label',
|
2026-06-05 19:14:54 +02:00
|
|
|
'Logo',
|
|
|
|
|
'Logo px',
|
|
|
|
|
'PREVIEW',
|
|
|
|
|
'Preview',
|
2026-06-11 19:25:05 +02:00
|
|
|
'Privacy',
|
2026-06-05 19:14:54 +02:00
|
|
|
'SLIDES',
|
|
|
|
|
'Slide',
|
|
|
|
|
'slide',
|
2026-06-08 13:51:29 +02:00
|
|
|
'Spider',
|
2026-06-05 19:14:54 +02:00
|
|
|
};
|
|
|
|
|
final expression = RegExp(r'''\.d\(\s*('(?:\\.|[^'])*'|"(?:\\.|[^"])*")''');
|
|
|
|
|
final files = Directory('lib')
|
|
|
|
|
.listSync(recursive: true)
|
|
|
|
|
.whereType<File>()
|
|
|
|
|
.where((file) => file.path.endsWith('.dart'));
|
|
|
|
|
final sources = <String>{};
|
|
|
|
|
|
|
|
|
|
for (final file in files) {
|
|
|
|
|
final content = file.readAsStringSync();
|
|
|
|
|
for (final match in expression.allMatches(content)) {
|
|
|
|
|
sources.add(_unquoteDartString(match.group(1)!));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final english = const AppLocalizations(Locale('en'));
|
|
|
|
|
final missing = sources.where((source) {
|
|
|
|
|
final translated = english.d(source);
|
|
|
|
|
return translated == source && !unchangedInEnglish.contains(source);
|
|
|
|
|
}).toList()..sort();
|
|
|
|
|
|
|
|
|
|
expect(missing, isEmpty);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('all literal Dutch source strings are translated in every language', () {
|
|
|
|
|
const unchangedInAllLanguages = {
|
|
|
|
|
'Accent / bullets',
|
|
|
|
|
'Bullet',
|
|
|
|
|
'Coverflow',
|
Add project docs, EUPL licence, and open-source licence check
Documentation & licensing:
- Add the EUPL-1.2 licence (LICENSE.md) and set the project licence; refresh
the README (name origin wink, updated feature list, documentation index).
- Add CONTRIBUTING, SECURITY, CODE_OF_CONDUCT, CHANGELOG, AUTHORS, and
THIRD_PARTY_NOTICES, plus docs/ (ARCHITECTURE, BUILD, USER_GUIDE, SHORTCUTS,
LICENSE_COMPLIANCE) and .github/ (CI workflow, issue/PR templates).
- Bring docs/FILE_FORMAT.md in line with current behaviour (code & chart
slides, per-slide TLP comment, annotation .ink.json sidecar, chart data/ CSVs).
Open-source compliance:
- Add tool/check_licenses.dart and a `make licenses` target (wired into
check-full and CI) that verifies every resolved dependency uses a recognised
open-source licence. A scan of all 151 packages and bundled assets found only
OSI-approved licences.
Charts (Fase 1.1):
- Replace the chart CSV textarea with an in-app editable data grid (editable
series/labels/values, add/remove row & column, read-only when linked).
- Centralize the linked-CSV directory name (`data/`) in a shared constant.
Also normalize formatting repo-wide with `dart format` and fix one
curly-braces lint, so `make check` and CI are green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 12:19:56 +02:00
|
|
|
'Label',
|
2026-06-05 19:14:54 +02:00
|
|
|
'Logo',
|
|
|
|
|
'Logo px',
|
|
|
|
|
'PREVIEW',
|
|
|
|
|
'Preview',
|
|
|
|
|
'SLIDES',
|
|
|
|
|
'Slide',
|
|
|
|
|
'slide',
|
|
|
|
|
};
|
|
|
|
|
final expression = RegExp(r'''\.d\(\s*('(?:\\.|[^'])*'|"(?:\\.|[^"])*")''');
|
|
|
|
|
final files = Directory('lib')
|
|
|
|
|
.listSync(recursive: true)
|
|
|
|
|
.whereType<File>()
|
|
|
|
|
.where((file) => file.path.endsWith('.dart'));
|
|
|
|
|
final sources = <String>{};
|
|
|
|
|
|
|
|
|
|
for (final file in files) {
|
|
|
|
|
final content = file.readAsStringSync();
|
|
|
|
|
for (final match in expression.allMatches(content)) {
|
|
|
|
|
sources.add(_unquoteDartString(match.group(1)!));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final missingByLanguage = <String, List<String>>{};
|
|
|
|
|
for (final languageCode in AppLocalizations.languageNames.keys) {
|
|
|
|
|
if (languageCode == 'nl') continue;
|
|
|
|
|
final missing = sources.where((source) {
|
|
|
|
|
if (unchangedInAllLanguages.contains(source)) return false;
|
|
|
|
|
return !AppLocalizations.hasDirectDutchSourceTranslation(
|
|
|
|
|
languageCode,
|
|
|
|
|
source,
|
|
|
|
|
);
|
|
|
|
|
}).toList()..sort();
|
|
|
|
|
if (missing.isNotEmpty) missingByLanguage[languageCode] = missing;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
expect(missingByLanguage, isEmpty);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String _unquoteDartString(String value) {
|
|
|
|
|
final quote = value[0];
|
|
|
|
|
final body = value.substring(1, value.length - 1);
|
|
|
|
|
return body
|
|
|
|
|
.replaceAll(r'\\', r'\')
|
|
|
|
|
.replaceAll('\\$quote', quote)
|
|
|
|
|
.replaceAll(r'\n', '\n')
|
|
|
|
|
.replaceAll(r'\r', '\r')
|
|
|
|
|
.replaceAll(r'\t', '\t');
|
2026-06-04 08:17:12 +02:00
|
|
|
}
|