Skip to content
This repository has been archived by the owner on Nov 7, 2023. It is now read-only.

Commit

Permalink
refactor: dynamically generate locales
Browse files Browse the repository at this point in the history
  • Loading branch information
zyrouge committed Nov 1, 2021
1 parent 852f0fe commit e29fa19
Show file tree
Hide file tree
Showing 30 changed files with 1,291 additions and 603 deletions.
22 changes: 22 additions & 0 deletions cli/commands/generator/run/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import chalk from "chalk";
import { Logger } from "../../../logger";

import { runDartBuildRunner } from "./tasks/build_runner";
import { generateLocales } from "./tasks/locale";
import { generateMeta } from "./tasks/meta";

const logger = new Logger("generator:run");

const tasks: (() => Promise<void>)[] = [
runDartBuildRunner,
generateLocales,
generateMeta,
];

export const generate = async () => {
logger.log(`Starting ${chalk.cyanBright(tasks.length)} tasks...`);

await Promise.all(tasks.map((x) => x()));

logger.log(`Finished running ${chalk.cyanBright(tasks.length)} tasks`);
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import {
readFile,
writeFile,
} from "fs-extra";
import { SpawnError, spawn } from "../../../spawn";
import { config } from "../../../config";
import { Logger } from "../../../logger";
import { SpawnError, spawn } from "../../../../spawn";
import { config } from "../../../../config";
import { Logger } from "../../../../logger";

const logger = new Logger("build_runner:generate");
const logger = new Logger("generator:run:build_runner");

const generated = {
from: join(config.base, "lib"),
Expand Down Expand Up @@ -106,8 +106,8 @@ const execute = async (force: boolean) => {
}
};

export const generate = async () => {
logger.log("Running build_runner command...");
export const runDartBuildRunner = async () => {
logger.log("Running dart build_runner command...");

try {
await execute(process.argv.includes("-f"));
Expand All @@ -129,5 +129,5 @@ export const generate = async () => {
}
}

logger.log("Finished running build_runner command");
logger.log("Finished running dart build_runner command");
};
163 changes: 163 additions & 0 deletions cli/commands/generator/run/tasks/locale.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import { dirname, join } from "path";
import chalk from "chalk";
import got from "got";
import { Octokit } from "@octokit/rest";
import { ensureDir, readFile, writeFile, lstat } from "fs-extra";
import { config } from "../../../../config";
import { Repos } from "../../../../constants";
import { Logger } from "../../../../logger";

const logger = new Logger("generator:run:locale");

export const localeFile = join(
config.base,
"packages/utilx/lib/generated/locale.g.dart"
);

const reservedIdentifiers = ["as", "do", "in", "is"];

export const generateLocales = async () => {
let previousContent: string | undefined;
try {
previousContent = (await readFile(localeFile)).toString();
} catch (err: any) {
if (err.code != "ENOENT") {
throw err;
}
}

const previousSha = previousContent?.match(
/\/\/ SHA: (\b[0-9a-f]{5,40}\b)/
)?.[1];
const previousId = previousContent?.match(
/\/\/ ID: ([A-Za-z0-9+\/=]+)/
)?.[1];

const fileStat = await lstat(__filename);
const latestId = Buffer.from(
`${fileStat.ctimeMs}-${fileStat.mtimeMs}`
).toString("base64");

const octokit = new Octokit();
const {
data: [{ sha: latestSha }],
} = await octokit.request("GET /repos/{owner}/{repo}/commits", {
...Repos.extensionsStore.repo,
per_page: 1,
});

if (previousSha === latestSha && previousId === latestId) {
logger.log(`Aborting update due to matching SHA and ID`);
return;
}

const { body } = await got.get(
Repos.extensionsStore.languagesJson(latestSha),
{
responseType: "json",
}
);

const {
countries,
languages,
}: {
countries: Record<string, string>;
languages: Record<string, string>;
} = body as any;

const content = `
// SHA: ${latestSha}
// ID: ${latestId}
// Generated file. Do not edit.
${getLanguagesClass(languages)}
${getCountriesClass(countries)}
`.trim();

await ensureDir(dirname(localeFile));
await writeFile(localeFile, content);
logger.log(`Generated ${chalk.cyanBright(localeFile)}`);
};

const getLanguagesClass = (languages: Record<string, string>) => {
return `
enum LanguageCodes {
${Object.keys(languages)
.map((x) => ` ${escapeIdentifier(x)}`)
.join(",\n")}
}
extension LanguageCodesUtils on LanguageCodes {
String get code => name.replaceFirst(RegExp(r'_$'), '');
String get language => LanguageCodesUtils.codeNameMap[this]!;
}
abstract class LanguageUtils {
static Map<LanguageCodes, String> codeNameMap =
<LanguageCodes, String>{
${Object.entries(languages)
.map(
([code, lang]) =>
` LanguageCodes.${escapeIdentifier(code)}: '${lang.replace(
/'/,
"\\'"
)}'`
)
.join(",\n")}
};
static final Map<String, LanguageCodes> nameCodeMap =
LanguageCodes.values
.asMap()
.map(
(final int k, final LanguageCodes v) =>
MapEntry<String, LanguageCodes>(v.name, v),
)
.cast<String, LanguageCodes>();
}
`.trim();
};

const getCountriesClass = (countries: Record<string, string>) => {
return `
enum CountryCodes {
${Object.keys(countries)
.map((x) => ` ${escapeIdentifier(x)}`)
.join(",\n")}
}
extension CountryCodesUtils on CountryCodes {
String get code => name.replaceFirst(RegExp(r'_$'), '');
String get language => CountryCodesUtils.codeNameMap[this]!;
}
abstract class CountryUtils {
static Map<CountryCodes, String> codeNameMap =
<CountryCodes, String>{
${Object.entries(countries)
.map(
([code, country]) =>
` CountryCodes.${escapeIdentifier(code)}: '${country.replace(
/'/,
"\\'"
)}'`
)
.join(",\n")}
};
static final Map<String, CountryCodes> nameCodeMap =
CountryCodes.values
.asMap()
.map(
(final int k, final CountryCodes v) =>
MapEntry<String, CountryCodes>(v.name, v),
)
.cast<String, CountryCodes>();
}
`.trim();
};

const escapeIdentifier = (value: string) =>
reservedIdentifiers.includes(value) ? `${value}_` : value;
Original file line number Diff line number Diff line change
@@ -1,27 +1,29 @@
import { dirname, join } from "path";
import chalk from "chalk";
import { ensureDir, readFile, writeFile } from "fs-extra";
import { parse as yaml } from "yaml";
import { config } from "../../../config";
import { Logger } from "../../../logger";
import { config } from "../../../../config";
import { Logger } from "../../../../logger";

const logger = new Logger("meta:generate");
const logger = new Logger("generator:run:meta");

export const generate = async () => {
const metaFile = join(config.base, "assets/data/meta.json");

export const generateMeta = async () => {
const pubspecPath = join(config.base, "pubspec.yaml");
logger.log(`Reading pubspec.yaml from ${pubspecPath}`);

const pubspec = yaml((await readFile(pubspecPath)).toString());

const outPath = join(config.base, "assets/data/meta.json");
await ensureDir(dirname(outPath));
await ensureDir(dirname(metaFile));
await writeFile(
outPath,
metaFile,
JSON.stringify({
name: pubspec.description,
code: pubspec.name,
version: pubspec.version,
})
);

logger.log(`Generated ${outPath}`);
logger.log(`Generated ${chalk.cyanBright(metaFile)}`);
};
4 changes: 0 additions & 4 deletions cli/commands/meta/generate/run.ts

This file was deleted.

14 changes: 14 additions & 0 deletions cli/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const Repos = {
extensionsStore: {
repo: {
owner: "yukino-app",
repo: "extensions-store",
},
commitsApiURL() {
return `https://api.github.com/repos/${this.repo.owner}/${this.repo.repo}/commits?per_page=1`;
},
languagesJson(sha: string) {
return `https://raw.githubusercontent.com/${this.repo.owner}/${this.repo.repo}/${sha}/lib/assets/languages.json`;
},
},
};
2 changes: 1 addition & 1 deletion cli/hooks/hooks.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"macos:run",
"macos:build"
],
"commands": ["meta:generate", "build_runner:generate"]
"commands": ["generator:run"]
},
{
"type": "prerun",
Expand Down
7 changes: 1 addition & 6 deletions cli/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,7 @@ export const run = async (fn: () => Promise<void>) => {
console.error(chalk.redBright(err));

if (err instanceof Error && err.stack) {
const stackLines = err.stack.split("\n");
if (stackLines[0] === err.toString()) {
err.stack = stackLines.slice(1).join("\n");
}

console.error(chalk.gray(err.stack));
console.error(chalk.gray(err.stack.replace(err.message, "")));
}

console.log(
Expand Down
49 changes: 2 additions & 47 deletions lib/modules/translator/translations.dart
Original file line number Diff line number Diff line change
@@ -1,53 +1,8 @@
import 'package:flutter/material.dart';
import 'package:utilx/utilities/countries.dart';
import 'package:utilx/utilities/languages.dart';

class TranslationLocale {
TranslationLocale(this.code, [this.country]);

factory TranslationLocale.parse(final String locale) {
final RegExpMatch match = RegExp('([^_]+)(_(.*))?').firstMatch(locale)!;
final LanguageCodes lang = LanguageUtils.languageCodeMap[match.group(1)!]!;
final LanguageCountries? country = match.group(3) != null
? LanguageCountryUtils.nameCodeMap[match.group(3)!]
: null;

return TranslationLocale(lang, country);
}

final LanguageCodes code;
final LanguageCountries? country;

int compare(final TranslationLocale locale) {
int threshold = 0;

if (locale.code == code) {
threshold += 1;
}

if (locale.country == country) {
threshold += 2;
}

return threshold;
}

@override
String toString() =>
<String>[code.name, if (country != null) country!.name].join('_');

@override
bool operator ==(final Object other) =>
other is TranslationLocale &&
code == other.code &&
country == other.country;

@override
int get hashCode => Object.hash(code, country);
}
import 'package:utilx/utilities/locale.dart';

abstract class TranslationSentences {
TranslationLocale get locale;
Locale get locale;
TextDirection get textDirection => TextDirection.ltr;

String home();
Expand Down
4 changes: 2 additions & 2 deletions lib/modules/translator/translations/en.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import 'package:utilx/utilities/languages.dart';
import 'package:utilx/utilities/locale.dart';
import '../translations.dart';

class Sentences extends TranslationSentences {
@override
TranslationLocale get locale => TranslationLocale(LanguageCodes.en);
Locale get locale => Locale(LanguageCodes.en);

@override
String home() => 'Home';
Expand Down
7 changes: 2 additions & 5 deletions lib/modules/translator/translations/pt_br.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import 'package:utilx/utilities/countries.dart';
import 'package:utilx/utilities/languages.dart';
import 'package:utilx/utilities/locale.dart';
import './en.dart' as en;
import '../translations.dart';

class Sentences extends en.Sentences {
@override
TranslationLocale get locale =>
TranslationLocale(LanguageCodes.pt, LanguageCountries.br);
Locale get locale => Locale(LanguageCodes.pt, CountryCodes.br);

@override
String home() => 'Menu';
Expand Down

0 comments on commit e29fa19

Please sign in to comment.